#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Logger.hpp"
#include "InetAddr.hpp"

const static int defaultfd = -1;
const static int backlog=16;

enum
{
    OK,
    SOCKET_CREATE_ERRO,
    SOCKET_BIND_ERRO,
    SOCKET_LISTEN_ERRO
};

// 模板继承模式
class Socket
{
public:
    virtual ~Socket() = default;
    virtual void CreateSocket() = 0;
    virtual void BindSocket(uint16_t port) = 0;
    virtual void ListenSocket() = 0;
    virtual int Accept(InetAddr*addr)=0;
    virtual int SockFd()=0;
public:
    void BuildTcpSocket(uint16_t port)
    {
        CreateSocket();
        BindSocket(port);
        ListenSocket();
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket() : _sockfd(defaultfd)
    {
    }
    void CreateSocket() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket创建失败";
            exit(SOCKET_CREATE_ERRO);
        }
        // 设置地址复用
        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        LOG(LogLevel::INFO) << "socket创建成功";
    }
    void BindSocket(uint16_t port)override
    {
        InetAddr local(port);
        if (bind(_sockfd,local.Addr(),local.Len())!=0)
        {
            LOG(LogLevel::FATAL)<<"bind socket失败";
            exit(SOCKET_BIND_ERRO);
        }
        LOG(LogLevel::INFO)<<"bind socket成功";
    }
    void ListenSocket() override
    {
        if(listen(_sockfd,backlog)!=0)
        {
            LOG(LogLevel::FATAL)<<"listen socket失败";
            exit(SOCKET_LISTEN_ERRO);
        }
        LOG(LogLevel::INFO)<<"listen socket成功";
    }
    int Accept(InetAddr*addr)override
    {
        struct sockaddr_in peer;
        socklen_t len=sizeof(peer);
        int fd=accept(_sockfd,(struct sockaddr*)&peer,&len);
        if(fd<0)
        {
            LOG(LogLevel::DEBUG)<<"accept socket失败";
            return -1;
        }
        addr->Init(peer);
        return fd;
    }
    int SockFd()override
    {
        return _sockfd;
    }
    ~TcpSocket() {}

private:
    int _sockfd;
};